#ifndef _KLAS_POST_PROCESSOR_HPP_
#define _KLAS_POST_PROCESSOR_HPP_

#include "RenderToTexture.hpp"
#include "ShaderProgram.hpp"

#include <vector>
using std::vector;

namespace klas
{
  namespace graphics
  {
    //! Allows batching for post-processing effects that require multiple passes.
    /*! Often, post-processing requires many passes of the same texture.  This eases that requirement.
        This is a utility class that wraps ShaderProgram, Shader, and RenderToTexture. */
    class PostProcessorBatch
    {
    public:

      //! Ctor.  Doesn't do much.
      PostProcessorBatch();

      //! A ShaderProgram and its alpha value.
      struct Batch
      {
        Batch(); //!< Basic ctor.  Doesn't do much.
        Batch(ShaderProgram* shader, decimal alpha = 1.0f);  //!< Create the batch from params.
        ShaderProgram* shader;  //!< The shader program to apply to the frame.
        decimal alpha;          //!< How much of the new texture should blend with the old one? (0.0 to 1.0)
      };

      //! Create the batch from user definied parameters.
      /*! \param batches A vector of shaders and alpha values.  The batches are executed in order.
          \param finalEffectAlpha The total strength of the batch on the orignal texture.
          \sa setFinalEffectAlpha(decimal alpha)
          \sa addEffect(ShaderProgram* effect, decimal alpha) */
      PostProcessorBatch(const vector<Batch>& batches, decimal finalEffectAlpha = 1.0f);

      //! How much should the new texture (after all effects) blend with the old one?
      void setFinalEffectAlpha(decimal alpha);

      //! Adds an effect and an alpha (a batch) to the batch.  Effects are executed in sequential order.
      /*! \param effect The shader effect to apply to the texture.
          \param alpha How much should the new texture (after the shader has been applied) blend with the previous texture? */
      void addEffect(ShaderProgram* effect, decimal alpha = 1.0f);

      //! Adds a batch to the batch.  Batches are executed in sequential order.
      void addBatch(const Batch& batch);

      //! Internal use: Render the effects.
      /*! \param renderToA The texture to render the effects onto.  renderToA will always contain the most current texture.
          \param renderToB The texture to render the effects onto. */
      void renderEffects(RenderToTexture* renderToA, RenderToTexture* renderToB, const math::Vector2d& numSides);


    private:

      vector<Batch> m_effects;  //!< The shaders that need to be applied to the texture.  Performed in sequential order.
      decimal m_finalEffectAlpha; //!< How much should this new texture (after all shaders have been applied) affect the texture the batch is rendering to?

    };


    //! A wrapper class around PostProcessorBatch.  Makes post-processing even easier to do.
    class PostProcessor
    {
    public:

      //! Ctor.
      /*! \param dim The dimensions of the area to post processes.  Most likely window dimensions. */
      PostProcessor(const math::Vector2<uint32>& dim);

      //! Dtor.
      ~PostProcessor();

      //! Allows the post-processor to capture the screen.
      /*! Make sure you call this method before rendering anything you want post-processed. */
      void startedRendering();

      //! Stops the post-processor from continuing to capture the screen.
      /*! Make sure you call this method after you are done rendering anything you want post-processed. */
      void finishedRendering();

      //! Applies a "shader batch" or post-processing batch to the rendered window.
      /*! Batches are applied in sequential order.  Please note that this method will call 
          finishedRendering if rendering is not disabled. 
          NOTE: The name of the screen texture in the shader is tex0
          \sa finishedRendering

          \param batch The next effect batch to render.
          \param quads How many quads to split the screen up into.  {1, 1} implies
                       that the shader will get 4 verticies, {2, 2} -> 16, {x, y} -> 4xy verticies */
      void addBatch(const PostProcessorBatch& batch, const math::Vector2d& quads);

      //! Renders the post processor, with all effects applied.
      void render();

    private:

      RenderToTexture* m_textureA, //!< The screen texture that is rendered to, and the effects are applied to.  This is the first of two elements.
                     * m_textureB; //!< The screen texture that is rendered to, and the effects are applied to.  This is the second of two elements.
      vector<PostProcessorBatch> m_batches; //!< The effects that need to be applied to the texture.
      vector<math::Vector2d> m_batchQuads;
    };
  }
}

#endif